package undecided;

import java.awt.Color;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.font.TextAttribute;

import java.text.AttributedString;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;

import javax.swing.*;

import View.MasterView;
import View.MasterViewPanel;
import View.Views;

import rectangles.BubbleShieldRectangle;
import rectangles.CrateRectangle;
import rectangles.FireRingRectangle;
import rectangles.IceBlockRectangle;
import rectangles.ImmovableBlockRectangle;
import rectangles.ProjectileRectangle;
import rectangles.SpeedBoostRectangle;
import rectangles.SpikePitRectangle;
import rectangles.TNTRectangle;
import rectangles.TankRectangle;

/**
 * 
 * This class is the primary battle view in which the player controls a single
 * tank that can move through the arrow keys and shoot through the mouse.
 * 
 * @author Team Exception
 * 
 * @extends MasterViewPanel
 * 
 * @implements Observer
 * 
 * @see MasterViewPanel, MasterView, LanView, TitleView
 * 
 */
public class TankView extends MasterViewPanel implements Observer {

	// declaring instance varaibles
	private static final long serialVersionUID = 1L;
	private JPanel panel;
	private Map currentMap;
	private Image dbImage;
	private Graphics dbg;
	private PlayerTank player;
	private EnemyTank enemy;
	private LinkedList<Projectile> projectileList;
	private LinkedList<Obstacle> obstacleList;
	private LinkedList<PlayerTank> tankList;
	private LinkedList<Item> itemList;
	private LinkedList<EnemyTank> enemyList;
	private LinkedList<Explosion> explosionList;
	java.util.Vector<Projectile> pVector; // a vector of projectiles
	private boolean won, lost, gameOver;
	private Image camo, wheel, steel, gold, grass, ice, sand;

	/**
	 * This is the class constructor for the TankView Class. It contains a call
	 * to the superclass MasterView and the map to which the TankView is to
	 * include. It not only contains the battlefield that the tanks will fight
	 * on but also the amount of lives the player still has (this will be
	 * campaign mode), the level the user is on, and the active items on the
	 * field.
	 * 
	 * @param m
	 *            this is the MasterView
	 * @param map
	 *            the map to which this TankView is to include
	 */
	public TankView(MasterView m, Map map) {
		super(m);
		currentMap = map;
		camo = new ImageIcon("images/camo.png").getImage();
		wheel = new ImageIcon("images/wheel-md.png").getImage();
		steel = new ImageIcon("images/steel.png").getImage();
		gold = new ImageIcon("images/gold.png").getImage();
		grass = new ImageIcon("images/grass.png").getImage();
		ice = new ImageIcon("images/ice.png").getImage();
		sand = new ImageIcon("images/sand.png").getImage();
		won = false;
		lost = false;
		gameOver = false;
		currentMap.addObserver(this);
		tankList = currentMap.getPlayers();
		explosionList = currentMap.getExplosions();
		projectileList = currentMap.getProjectiles();
		obstacleList = currentMap.getObstacles();
		enemyList = currentMap.getEnemies();
		itemList = currentMap.getItems();
		currentMap.enemyList.getFirst().startEnemyTank();
		GameThread gt = new GameThread();
		gt.start();

		this.setFocusable(true);
		panel = new JPanel();
		player = tankList.getFirst();
		add(panel);
		addKeyListener(new moveAndShootListener());// adding the movement and

		Handlerclass handler = new Handlerclass();// creating the mouse handler
													// class
		this.addMouseListener(handler);// adding mouse actions to be detected on
										// the java panel
		this.addMouseMotionListener(handler);// adding mouse motion to be
												// detected on the java panel
		this.setVisible(true);

	}

	/**
	 * This is basically the private inner class that handles the mouse listener
	 * and mouse motion listener methods that controls the direction and whether
	 * or not the tank is shooting.
	 * 
	 * @author Team Exception
	 * 
	 * @implements MouseListener, MouseMotionListener
	 * 
	 * @see MouseListener, MouseMotionListener
	 * 
	 */
	private class Handlerclass implements MouseListener, MouseMotionListener {

		@Override
		public void mouseDragged(MouseEvent arg0) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseMoved(MouseEvent arg0) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseClicked(MouseEvent arg0) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseEntered(MouseEvent arg0) {
			// TODO Auto-generated method stub

		}

		@Override
		public void mouseExited(MouseEvent arg0) {
			// TODO Auto-generated method stub

		}

		/**
		 * This method implements the MouseMotionListener and whenever the mouse
		 * is pressed, it will calculate the best fit direction in which the
		 * projectile is to travel.
		 * 
		 * @param arg0
		 *            mouse event argument
		 */
		public void mousePressed(MouseEvent arg0) {
			int count = 0;
			for (Projectile p : currentMap.getProjectiles()) {
				if (p instanceof PlayerProjectile) {
					count++;
				}
			}

			if (count == 0) {
				// finding difference in player and target location
				int xdiff = arg0.getX() - player.getLocation().col;
				int ydiff = arg0.getY() - player.getLocation().row;

				// calculating the distance between the player and the mouse
				double length = Math.sqrt(xdiff * xdiff + ydiff * ydiff);

				// create a new shot, with position relative to location of
				// tank,
				// the speed in the x and y directions
				player.shoot(
						new Point(player.getLocation().row, player
								.getLocation().col),
						(int) (xdiff * (5 / length)),
						(int) (ydiff * (5 / length)));

				// player.shoot();
			}
		}

		@Override
		public void mouseReleased(MouseEvent arg0) {
			// TODO Auto-generated method stub

		}

	}

	/**
	 * This method paints the TankView graphics when called.
	 * 
	 * @param g
	 *            Graphics component for this panel
	 */
	public void paint(Graphics g) {
		try {
			dbImage = createImage(getWidth(), getHeight());
			dbg = dbImage.getGraphics();
			paintComponent(dbg);
			g.drawImage(dbImage, 0, 0, this);
		} catch (Exception e) {

		}
	}

	/**
	 * This method will paint all the components of the TankView when called
	 * including obstacles, items, the tanks, and the field background along
	 * with the score board on the right hand side of the screen.
	 * 
	 * @param g
	 *            graphics component that java uses to paint components. It will
	 *            repaint all the components including the projectile, the tanks
	 *            in the tank list, and all the objects in the obstacle list.
	 */
	public void paintComponent(Graphics g) {
		if (MasterView.currentLevel == 1 || MasterView.currentLevel == 2) {
			for (int i = 0; i < 700; i += 50) {
				for (int j = 0; j < 1200; j += 50) {
					g.drawImage(grass, j, i, null);
				}
			}
		}
		if (MasterView.currentLevel == 3 || MasterView.currentLevel == 4) {
			for (int i = 0; i < 700; i += 50) {
				for (int j = 0; j < 1200; j += 50) {
					g.drawImage(sand, j, i, null);
				}
			}
		}
		if (MasterView.currentLevel == 5) {
			for (int i = 0; i < 700; i += 50) {
				for (int j = 0; j < 1200; j += 50) {
					g.drawImage(ice, j, i, null);
				}
			}
		}

		for (int i = 0; i < obstacleList.size(); i++) {
			Obstacle p = obstacleList.get(i);
			if (p instanceof SpikePit) {// for instance of SpikePit
				SpikePit sp = (SpikePit) p;
				SpikePitRectangle spRect = sp.getRectangle();
				g.drawImage(spRect.getImage(), spRect.xCoord(),
						spRect.yCoord(), null);
			}
		}

		for (Item p : itemList) {
			if (p instanceof SpeedBoost) {
				SpeedBoost s = (SpeedBoost) p;
				SpeedBoostRectangle tRect = s.getRectangle();
				g.drawImage(tRect.getImage(), tRect.xCoord(), tRect.yCoord(),
						null);
			}
			if (p instanceof IceBlock) {
				IceBlock s = (IceBlock) p;
				IceBlockRectangle tRect = s.getRectangle();
				g.drawImage(tRect.getImage(), tRect.xCoord(), tRect.yCoord(),
						null);
			}
			if (p instanceof BubbleShield) {
				BubbleShield s = (BubbleShield) p;
				BubbleShieldRectangle tRect = s.getRectangle();
				g.drawImage(tRect.getImage(), tRect.xCoord(), tRect.yCoord(),
						null);
			}
		}
		for (int i = 0; i < obstacleList.size(); i++) {
			Obstacle p = obstacleList.get(i);

			if (p instanceof Crate) {// for instance of crate
				Crate c = (Crate) p;
				CrateRectangle cRect = c.getRectangle();
				g.drawImage(cRect.getImage(), cRect.xCoord(), cRect.yCoord(),
						null);
			}
			if (p instanceof ImmovableBlock) {// for instance of immovableBlock
				ImmovableBlock ib = (ImmovableBlock) p;
				ImmovableBlockRectangle ibRect = ib.getRectangle();
				g.drawImage(ibRect.getImage(), ibRect.xCoord(),
						ibRect.yCoord(), null);
			}
			if (p instanceof FireRing) {// for instance of fireRing
				FireRing fr = (FireRing) p;
				FireRingRectangle frRect = fr.getRectangle();
				g.setColor(frRect.setColor());
				g.drawImage(fr.getImage(), frRect.xCoord(), frRect.yCoord(),
						null);
			}
			if (p instanceof TNT) {// for instance of TNT
				TNT tnt = (TNT) p;
				TNTRectangle tntRect = tnt.getRectangle();
				g.drawImage(tntRect.getImage(), tntRect.xCoord(),
						tntRect.yCoord(), null);
			}

		}
		for (PlayerTank p : tankList) {
			TankRectangle tRect = p.getRectangle();
			g.drawImage(p.getImage(), tRect.xCoord(), tRect.yCoord(), null);
		}
		for (EnemyTank p : enemyList) {
			TankRectangle tRect = p.getRectangle();
			g.drawImage(p.getImage(), tRect.xCoord(), tRect.yCoord(), null);
		}
		for (Projectile p : projectileList) {
			if (p instanceof PlayerProjectile) {
				PlayerProjectile s = (PlayerProjectile) p;
				ProjectileRectangle rect = s.getRectangle();
				g.drawImage(rect.getImage(), rect.xCoord(), rect.yCoord(), null);
			}
			if (p instanceof EnemyProjectile) {
				EnemyProjectile s = (EnemyProjectile) p;
				ProjectileRectangle rect = s.getRectangle();
				g.drawImage(rect.getImage(), rect.xCoord(), rect.yCoord(), null);
			}
		}
		for (Explosion p : explosionList) {
			g.drawImage(p.getImage(), p.getLocation().col, p.getLocation().row,
					null);
		}

		if (won == true) {
			Font font = new Font("Times New Roman", Font.BOLD, 28);
			String jb = "Mission Complete!";
			AttributedString att = new AttributedString(jb);
			att.addAttribute(TextAttribute.FOREGROUND, Color.YELLOW);
			att.addAttribute(TextAttribute.FONT, font);
			g.drawString(att.getIterator(), 400, 350);
		}
		if (lost == true) {
			Font font = new Font("Times New Roman", Font.BOLD, 28);
			String jb = "Mission Failed!";
			AttributedString att = new AttributedString(jb);
			att.addAttribute(TextAttribute.FOREGROUND, Color.RED);
			att.addAttribute(TextAttribute.FONT, font);
			g.drawString(att.getIterator(), 400, 350);
		}
		if (gameOver == true) {
			Font font = new Font("Times New Roman", Font.BOLD, 44);
			String jb = "Game Over!";
			AttributedString att = new AttributedString(jb);
			att.addAttribute(TextAttribute.FOREGROUND, Color.ORANGE);
			att.addAttribute(TextAttribute.FONT, font);
			g.drawString(att.getIterator(), 370, 350);
		}

		for (int i = 0; i < 700; i += 50) {
			for (int j = 985; j < 1200; j += 50) {
				if (i == 150 || i == 200 || i == 350 || i == 400) {
					g.drawImage(steel, j, i, null);
				} else {
					g.drawImage(camo, j, i, null);
				}
			}
		}
		for (int i = 690; i < 900; i += 20) {
			for (int j = 0; j < 1200; j += 20) {

				g.drawImage(gold, j, i, null);

			}
		}
		for (int i = 0; i < 700; i += 20) {
			for (int j = 985; j < 1200; j += 20) {
				if (i == 0 || i == 680 || j == 985 || j == 1165) {
					g.drawImage(gold, j, i, null);
				}
			}
		}
		for (int i = 0; i < 900; i += 20) {
			for (int j = 1180; j < 1500; j += 20) {
				g.drawImage(gold, j, i, null);

			}
		}

		Font font = new Font("Times New Roman", Font.BOLD, 20);
		String lives = "Lives Remaning";
		AttributedString att = new AttributedString(lives);
		att.addAttribute(TextAttribute.FOREGROUND, Color.WHITE);
		att.addAttribute(TextAttribute.FONT, font);
		g.drawString(att.getIterator(), 1018, 44);

		for (int i = 0; i < MasterView.playerLives; i++) {
			for (int j = 0; j < MasterView.playerLives * 50; j += 55) {
				g.drawImage(wheel, 1005 + j, 65, null);
			}
		}
		String curr = "Current Level: " + currentMap.getLevelNumber();
		AttributedString att3 = new AttributedString(curr);
		att3.addAttribute(TextAttribute.FOREGROUND, Color.WHITE);
		att3.addAttribute(TextAttribute.FONT, font);
		g.drawString(att3.getIterator(), 1013, 300);

		String item = "Active Items";
		AttributedString att6 = new AttributedString(item);
		att6.addAttribute(TextAttribute.FOREGROUND, Color.WHITE);
		att6.addAttribute(TextAttribute.FONT, font);
		g.drawString(att6.getIterator(), 1030, 485);

		if (player.isActiveShield()) {
			g.drawImage(new BubbleShieldRectangle(-10, -10).getImage(), 1020,
					515, null);
		}
		if (player.isActiveBoost()) {
			g.drawImage(new SpeedBoostRectangle(-10, -10).getImage(), 1100,
					515, null);
		}

	}

	/**
	 * This private inner class controls the player controlled tank allowing it
	 * to move via key listeners and shoot through the mouse lister. The tank
	 * will move on key pressed.
	 * 
	 * @author Team Exception
	 * 
	 * @see KeyListener
	 * 
	 */
	private class moveAndShootListener implements KeyListener {

		/**
		 * This method will call the methods to move the PlayerTank depending on
		 * which key is pressed. NOTE: The keys for up, down, right, and left
		 * movement are w, s, d, and s respectively.
		 * 
		 * @param e
		 *            keyaction event for when a key is pressed
		 */
		@Override
		public void keyPressed(KeyEvent e) {
			int keyEvent = e.getKeyCode();
			if (keyEvent == KeyEvent.VK_W) {
				player.moveUp();
			}
			if (keyEvent == KeyEvent.VK_S) {
				player.moveDown();
			}
			if (keyEvent == KeyEvent.VK_A) {
				player.moveLeft();
			}
			if (keyEvent == KeyEvent.VK_D) {
				player.moveRight();
			}

		}

		@Override
		public void keyReleased(KeyEvent e) {

		}

		@Override
		public void keyTyped(KeyEvent e) {

		}

	}

	/**
	 * This method will be notified when the observed are called and will either
	 * remove dead obstacles and repaint the projectiles.
	 * 
	 * @param v
	 *            observable variable
	 * 
	 * @param o
	 *            object that is to be analyzed for update
	 */
	public synchronized void update(Observable v, Object o) {
		if (o == null) {
			repaint();
		}
		if (o instanceof Point) {
			Point p = (Point) o;
			Explosion et = new Explosion(p, currentMap);
			currentMap.addExplosion(et);

		}

	}

	/**
	 * This method will deal with the primary tank view thread. When the battle
	 * is to begin, this thread is started, constructing the map, turning on the
	 * ItemCreator, and the AI tank movement.
	 * 
	 * @category inner class
	 * 
	 * @author Team Exception
	 * 
	 * @extends Thread
	 * 
	 * @see Map, ItemCreator, PlayerTank, EnemyTank
	 * 
	 */
	private class GameThread extends Thread {

		private boolean exists;

		public GameThread() {
			exists = true;
		}

		/**
		 * This method is the thread that is to be run
		 */
		@Override
		public synchronized void run() {
			while (exists) {

				if (currentMap.getPlayers().size() == 0) {
					lost = true;
					MasterView.playerLives--;
					if (MasterView.playerLives == -1) {
						lost = false;
						gameOver = true;
						repaint();
						try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {

						}

						m.changeView(Views.TITLE, null);
						MasterView.currentLevel = 1;
						MasterView.playerLives = 3;
						break;
					}
					repaint();
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {

					}
					m.changeView(Views.TANKVIEW, null);
					exists = false;
				} else if (currentMap.getEnemies().size() == 0) {
					won = true;
					repaint();
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {

					}

					MasterView.currentLevel++;
					m.changeView(Views.TANKVIEW, null);
					exists = false;
				} else {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {

					}
				}

			}

		}

	}

}
